#include "Mesh.h"
#include "MeshTriangle.h"

MeshTriangleUV::MeshTriangleUV()
{
	mesh_ptr = NULL;
}

//	index is for indexing different textures in mesh_ptr
MeshTriangleUV::MeshTriangleUV(Mesh* _Mesh, int p0, int p1, int p2, int index)
	: mesh_ptr(_Mesh)
{
	p[0] = p0;
	p[1] = p1;
	p[2] = p2;
}

MeshTriangleUV::~MeshTriangleUV()
{
	mesh_ptr = NULL;
}

bool MeshTriangleUV::hit(const Ray& r, float tmin, float tmax, float time, HitRecord &record) const
{
	Vector3 p0(mesh_ptr->vertUVs[p[0]].vertex);
	Vector3 p1(mesh_ptr->vertUVs[p[1]].vertex);
	Vector3 p2(mesh_ptr->vertUVs[p[2]].vertex);

	float a = p0.x() - p1.x();
	float b = p0.y() - p1.y();
	float c = p0.z() - p1.z();

	float d = p0.x() - p2.x();
	float e = p0.y() - p2.y();
	float f = p0.z() - p2.z();

	float g = r.direction().x();
	float h = r.direction().y();
	float i = r.direction().z();

	float j = p0.x() - r.origin().x();
	float k = p0.y() - r.origin().y();
	float l = p0.z() - r.origin().z();

	//	compute M
	float eihf = e * i - h * f;
	float gfdi = g * f - d * i;
	float dheg = d * h - e * g;

	float M = a * eihf + b * gfdi + c * dheg;
	float invM = 1.0f / M;

	//	compute beta
	float beta = (j * eihf + k * gfdi + l * dheg) * invM;
	if (beta <= 0.0f || beta >= 1.0f) return false;

	//	compute gamma
	float akjb = a * k - j * b;
	float jcal = j * c - a * l;
	float blkc = b * l - k * c;
	float gamma = (i * akjb + h * jcal + g * blkc) * invM;
	if (gamma <= 0.0f || beta + gamma >= 1.0f) return false;
	//	note that it is enough to test gamma <= 0.0f || beta + gamma >= 1.0f
	//	since we have known beta is in (0, 1)

	//	compute t
	float t = -invM * (f * akjb + e * jcal + d * blkc);
	if (t < tmin || t > tmax) return false;

	//	fill in the record
// 	record.t = t;
// 	record.normal = unitVector(cross(p1 - p0, p2 - p0));
// 	//record.color = color;
	float alpha = 1.0f - beta - gamma;
	Vector2 u0(mesh_ptr->vertUVs[p[0]].uv);
	Vector2 u1(mesh_ptr->vertUVs[p[1]].uv);
	Vector2 u2(mesh_ptr->vertUVs[p[2]].uv);
	record.uv = alpha * u0 + beta * u1 + gamma * u2;
	record.hit_tex = mesh_ptr->getTexture();
	record.t = t;
	record.normal = unitVector(cross(p1 - p0, p2 - p0));
	record.hit_p = r(t);
	return true;
}

bool MeshTriangleUV::shadowHit(const Ray& r, float tmin, float tmax, float time) const
{
	Vector3 p0(mesh_ptr->vertUVs[p[0]].vertex);
	Vector3 p1(mesh_ptr->vertUVs[p[1]].vertex);
	Vector3 p2(mesh_ptr->vertUVs[p[2]].vertex);

	float a = p0.x() - p1.x();
	float b = p0.y() - p1.y();
	float c = p0.z() - p1.z();

	float d = p0.x() - p2.x();
	float e = p0.y() - p2.y();
	float f = p0.z() - p2.z();

	float g = r.direction().x();
	float h = r.direction().y();
	float i = r.direction().z();

	float j = p0.x() - r.origin().x();
	float k = p0.y() - r.origin().y();
	float l = p0.z() - r.origin().z();

	//	compute M
	float eihf = e * i - h * f;
	float gfdi = g * f - d * i;
	float dheg = d * h - e * g;

	float M = a * eihf + b * gfdi + c * dheg;
	float invM = 1.0f / M;

	//	compute beta
	float beta = (j * eihf + k * gfdi + l * dheg) * invM;
	if (beta <= 0.0f || beta >= 1.0f) return false;

	//	compute gamma
	float akjb = a * k - j * b;
	float jcal = j * c - a * l;
	float blkc = b * l - k * c;
	float gamma = (i * akjb + h * jcal + g * blkc) * invM;
	if (gamma <= 0.0f || beta + gamma >= 1.0f) return false;
	//	note that it is enough to test gamma <= 0.0f || beta + gamma >= 1.0f
	//	since we have known beta is in (0, 1)

	//	compute t
	float t = -invM * (f * akjb + e * jcal + d * blkc);
	return (t >= tmin && t <= tmax);
}